package com.demo.web3j.controller;

import cn.hutool.core.util.StrUtil;
import com.blockchain.scanning.MagicianBlockchainScan;
import com.blockchain.scanning.biz.thread.EventThreadPool;
import com.blockchain.scanning.commons.config.rpcinit.impl.EthRpcInit;
import com.blockchain.tools.eth.codec.EthAbiCodecTool;
import com.blockchain.tools.eth.contract.util.EthContractUtil;
import com.blockchain.tools.eth.contract.util.model.SendModel;
import com.blockchain.tools.eth.contract.util.model.SendResultModel;
import com.demo.web3j.event.EventDemo;
import com.demo.web3j.utils.IPFSUtil;
import com.demo.web3j.utils.Web3jUtils;
import io.ipfs.api.IPFS;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.web3j.abi.EventEncoder;
import org.web3j.abi.FunctionEncoder;
import org.web3j.abi.FunctionReturnDecoder;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.*;
import org.web3j.abi.datatypes.generated.Uint256;
import org.web3j.crypto.*;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.DefaultBlockParameterNumber;
import org.web3j.protocol.core.methods.request.EthFilter;
import org.web3j.protocol.core.methods.request.Transaction;
import org.web3j.protocol.core.methods.response.*;
import org.web3j.protocol.exceptions.TransactionException;
import org.web3j.protocol.http.HttpService;
import org.web3j.tx.Transfer;
import org.web3j.tx.gas.DefaultGasProvider;
import org.web3j.utils.Convert;
import org.web3j.utils.Numeric;

import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutionException;

/**
 * 发送的交易只有三种状态 1.success 发送成功  2.pending 交易还在交易池中等待打包  3.交易拒绝（多种原因）
 * 但是这里注意下 并不是交易状态 为 success 就代表一定成功 这个只是代表交易成功的发送上链了 但是具体的执行逻辑业务逻辑成功与否就要看你的合约逻辑或者其他的 这个只是保证确实上链了具体的还是得去看交易详情
 * 交易过程的简要概述:
 * 1.创建交易 设置你要的交易数据 接收方 发送方等  2.签名交易  私钥签名你的交易  3.发送上链并且广播交易 矿工验证完后的共识机制  4.交易确认 矿工完成共识机制并且确定ok了后就会交易确认了 就是上面说到的success
 * 5.可以在浏览器上查询到交易哈希跟他对应的详情了
 * 所以 交易哈希 是在交易签名后生成的，但只有在该交易被矿工确认并包合在区块链上后才会真正的生效，此时才能在区块链浏览器上查询到交易的详细信息。如果在确认之前查询交易哈希，可能会得到"未找到"或"待确认"的状态 (pending)
 * 所以为什么交易哈希打印出来了,区块链浏览器上确查询不到可能的原因有几个
 * 1.浏览器卡了 2.交易被拒绝了 等
 *
 */
@RestController
@RequestMapping("test")
@Slf4j
public class TestController {

    @Autowired
    private Web3jUtils web3jUtils;

    @Autowired
    private IPFSUtil ipfsUtil;

    // ipfs的服务器地址和端口 我这里是自己启动了本地的ipfs
    private IPFS ipfs = new IPFS("/ip4/127.0.0.1/tcp/5001");


    /**
     * 使用web3j包 在区块链上新建账户 离线创建钱包且用的是web3j 所以生成的自然是evm钱包
     * @param walletPwd
     * @throws Exception
     */
    @PostMapping("newAccounts")
    public void newAccounts(String walletPwd) throws Exception {
        Bip39Wallet wallet;
        try {
            //钱包Json文件存放在 D:/wallet/下
            wallet = WalletUtils.generateBip39Wallet(walletPwd, new File("D:/wallet/"));
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception("创建EVM(以太坊)钱包失败");
        }
        //通过钱包密码与助记词获得钱包地址、公钥及私钥信息
        Credentials credentials = WalletUtils.loadBip39Credentials(walletPwd,
                wallet.getMnemonic());
        log.info("钱包地址:{}",credentials.getAddress());
        log.info("钱包私钥16进制字符串表示:{}",credentials.getEcKeyPair().getPrivateKey().toString(16));
        log.info("钱包公钥16进制字符串表示:{}",credentials.getEcKeyPair().getPublicKey().toString(16));
        log.info("保存文件名:{}",wallet.getFilename());
        log.info("12个单词的助记词:{}",wallet.getMnemonic());
    }

    //使用web3j包访问区块链链上数据 查询账户余额
    @PostMapping("accountsBanlance")
    public void accountsBanlance(String address) throws Exception {
        Web3j web3j = web3jUtils.getBscWeb3j();
        try {
            EthGetBalance ethGetBalance = web3j.ethGetBalance(address, DefaultBlockParameterName.LATEST).send();
            if (ethGetBalance != null) {
                System.out.println("账号地址：" + address);
                // 打印账户余额
                System.out.println("账号余额 精度未转换：" + ethGetBalance.getBalance());
                // 将单位转为以太
                System.out.println("账号余额 精度转换：" + Convert.fromWei(ethGetBalance.getBalance().toString(), Convert.Unit.ETHER) );
            }
        } catch (ConnectException e){
            e.printStackTrace();
            throw new ConnectException("################连接失败，客户端挂了");
        } catch (SocketTimeoutException exception){
            exception.printStackTrace();
            throw new SocketTimeoutException("###############连接超时，钱包地址有问题");
        }
    }


    //使用web3j转主币 方式一
    public void transferETH() throws ExecutionException, InterruptedException {
        Web3j web3j = Web3j.build(new HttpService("https://bsc-dataseed4.ninicoin.io/"));
        String senderPrivateKey = "私钥";
        Credentials credentials = Credentials.create(senderPrivateKey);
        String recipientAddress = "接收手续费的地址";
        BigInteger nonce = getNonce(web3j, credentials.getAddress());
        BigInteger gasPrice = DefaultGasProvider.GAS_PRICE;
        BigInteger gasLimit = DefaultGasProvider.GAS_LIMIT;
//        BigInteger value = BigInteger.valueOf(1000000000000000L); // 0.001 ETH in Wei
        // 要转移的 BNB 数量
        BigInteger value = BigDecimal.valueOf(0.01).multiply(BigDecimal.TEN.pow(18)).toBigInteger();
        for(int i=0;i<3;i++){
            System.out.println("当前nonce为: " + nonce );
            RawTransaction rawTransaction = RawTransaction.createEtherTransaction(
                    nonce, gasPrice, gasLimit, recipientAddress, value);
            byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
            String hexValue = Numeric.toHexString(signedMessage);
            EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(hexValue).sendAsync().get();
            String transactionHash = ethSendTransaction.getTransactionHash();
            System.out.println("Transaction Hash: " + transactionHash);
            nonce = nonce.add(new BigInteger("1"));
        }
    }

    private static BigInteger getNonce(Web3j web3j, String address) throws ExecutionException, InterruptedException {
        EthGetTransactionCount ethGetTransactionCount = web3j.ethGetTransactionCount(address, DefaultBlockParameterName.PENDING).sendAsync().get();
        return ethGetTransactionCount.getTransactionCount();
    }

    //使用web3j转主币 方式二 这个方式比方式一的好,因为我在抢fam的时候开多个线程(3个)每个线程用新的账户跟新的receiveAddress 只有一个有交易哈希 其他的交易哈希都是null 我不知道为什么
    public void transferETH2(String receiveAddress) throws Exception {
        Web3j bscWeb3j = Web3j.build(new HttpService("https://bsc-dataseed4.ninicoin.io/"));
        //转入手续费
        log.info( receiveAddress+ ": 转入手续费开始");
        Credentials credentials = Credentials.create("转出手续费的账户的私钥");
        // 要转移的 BNB 数量 早上8点是领取的高峰
        BigDecimal amountToTransfer = BigDecimal.valueOf(0.01).multiply(BigDecimal.TEN.pow(18));
        // 构造转账交易
        TransactionReceipt transactionReceipt = Transfer.sendFunds(
                        bscWeb3j, credentials, receiveAddress, amountToTransfer, Convert.Unit.WEI)
                .send();
        // 可以通过交易收据获取交易哈希等信息
        String transactionHash = transactionReceipt.getTransactionHash();
        log.info("Transaction Hash: " + transactionHash);
        log.info(receiveAddress + ": 转入手续费结束");
    }


    /**
     * 校验地址合法性(evm标准的地址合法性校验)
     * 参考 https://www.jianshu.com/p/cfe788edd8b6
     * @param input
     * @return
     */
    public boolean isETHValidAddress(String input) {
        if (StrUtil.isEmpty(input) || !input.startsWith("0x"))
            return false;
        return isValidAddress(input);
    }
    public static boolean isValidAddress(String input) {
        String cleanInput = Numeric.cleanHexPrefix(input);
        try {
            Numeric.toBigIntNoPrefix(cleanInput);
        } catch (NumberFormatException e) {
            return false;
        }
        return cleanInput.length() == 40;
    }


    /**
     * 使用web3j包测试访问 智能合约 的read函数 -- 查询智能合约代币余额
     * @throws Exception
     */
    @PostMapping("readContract")
    public void readContract() throws Exception {

        //节点
        Web3j web3j = web3jUtils.getBscWeb3j();

        //合约地址  -- 其实就是代币地址 毕竟我们写 solidity发币都是发的代币 比如通过继承erc20 / erc721等 所以代币本质就是这个合约 所以代币地址 = 合约地址
        String contractAddress ="0x0B9b2f469232c761113a184B4148D2294C4C2DCf";

        //我的地址
        String myAddress =  "0x71c77511401b1C7CB64c89823f54C5928C5A624A";

        List input = Arrays.asList(new Address(myAddress));

        //web3j包访问合约read类型方法 且合约中参数返回类型是 [] 解决方案
        //https://stackoverflow.com/questions/47601052/in-web3j-how-do-you-create-a-typereference-for-array-types-discovered-at-runtim
        //如果合约中的方法返回的类型是 address[]的话 这里返回值参数得这样写了
//        List output = Arrays.asList(TypeReference.makeTypeReference("address[]"));
        List output = Arrays.asList(new TypeReference<Uint256>() {});

        // 生成需要调用函数的data  函数名 入参 返回值类型
        Function function = new Function("balanceOf", input, output);
        String data = FunctionEncoder.encode(function);
        // 组建请求的参数
        EthCall response = web3j.ethCall(
                        Transaction.createEthCallTransaction("0x71c77511401b1C7CB64c89823f54C5928C5A624A", contractAddress, data),
                        DefaultBlockParameterName.LATEST)
                .send();
        // 解析返回结果 注意：解析的返回结果你别看他是个List 其实该List实际上数量一直只有一个 真正的数据是保存在  decode.get(0).getValue() 中的 即这个List中的第一个元素中
        List<Type> decode = FunctionReturnDecoder.decode(response.getValue(), function.getOutputParameters());
        //如果返回值类型是 address[]的 则这里获取数据得这样写
//        List<Address> address = (List<Address>) decode.get(0).getValue();
        decode.stream().forEach(e->{
            System.out.println(e.getTypeAsString()+"  -->  "+e.getValue());
        });

    }

    /**
     * 使用web3j包测试访问 智能合约 的write函数 --转账 代币 or/and 主币
     * @throws Exception
     */
    @PostMapping("writeContract")
    public void writeContract( ) throws Exception {
        //节点
        Web3j web3j = web3jUtils.getBscWeb3j();
        //合约地址
        String contractAddress = "0x0B9b2f469232c761113a184B4148D2294C4C2DCf";
        //我的地址
        String myAddress =  "0x71c77511401b1C7CB64c89823f54C5928C5A624A";
        //我的私钥
        String privateKey = "3403038c4d97d23bd67bb26e0015e123bf869b22c38f5b2f6bc7c0b70e9e371b";
        //收款人地址
        String receiveAdress = "0x45998d65609965978F9Da29A9b7dB424aC45e682";

        //快速連續送transaction需要用这个 这里面的操作方式，就是取得nonce後，不再去鏈上取新的值，下次交易直接做+1的動作。所以可想而知，在同一個帳號下，是不能在multi-thread使用。
//        FastRawTransactionManager fastRawTransactionManager = new FastRawTransactionManager();

        //查询当前区块链网络的链的chainId
        long chainId = web3j.ethChainId().send().getChainId().longValue();

        //根据私钥创建交易凭证
        Credentials credentials = Credentials.create(privateKey);
        // 生成需要调用函数的data
        // java通过web3j调用智能合约传递数组 https://blog.csdn.net/zhang0135789/article/details/110931303  如果需要用到数组的话可以参考这个
        List input = Arrays.asList(new Address(receiveAdress)
                , new Uint256(new BigInteger("5982")));

        List output = Arrays.asList(new TypeReference<Bool>() {
        });

//        如果没有返回值的话 这样写
//        List output = Arrays.asList();

        //生成需要调用函数的data  函数名 入参 返回值类型
        Function function = new Function("transfer", input, output);
        //加密data
        String data = FunctionEncoder.encode(function);
        //估算gasLimit
        BigInteger gasLimit = web3jUtils.estimateGasLimit(web3j,myAddress,contractAddress, data);
        BigInteger gasPrice = web3jUtils.getGasPrice(web3j);
        //正式请求
        RawTransaction rawTransaction = RawTransaction.createTransaction(web3jUtils.getNonce(web3j,myAddress),gasPrice, gasLimit, contractAddress, data);
        // 签名数据
        byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, chainId, credentials);
        String hexValue = Numeric.toHexString(signedMessage);
        EthSendTransaction response = web3j.ethSendRawTransaction(hexValue).send();
        // 查看是否有错误
        if (response.hasError()) {
            throw new Exception("trade hash: " + response.getTransactionHash() +
                    "\nerror: " + response.getError().getMessage());
        }
        log.info("function: {} data: {}", "转账 transfer", data);
        log.info("Gas fee: {} ETH", gasPrice);
        log.info("Trade Hash: {}", response.getTransactionHash());
        log.info("response:{}:",response.getTransactionHash());
    }


    /**
     * 使用web3j拿到指定块内的数据
     */
    @PostMapping("scanBlock")
    public void scanBlock(){
        try {
            Web3j web3j = web3jUtils.getBscWeb3j();
            //拿到节点最新的区块高度  ---可以缓存起来或者落库 然后下次就从这里开始继续往下扫就行就可以不用每次都从头了
//            BigInteger latestBlockNum = web3j.ethBlockNumber().send().getBlockNumber();

            //这里为了方便写一个有我们合约自己交易的块高度
            BigInteger latestBlockNum = new BigInteger("27716137");

            //合约地址
            String contractAddress = "0x0B9b2f469232c761113a184B4148D2294C4C2DCf";

            // 根据块高度拿到块数据
            // 第二个参数表示是否返回区块中的交易数据
            EthBlock.Block block = web3j.ethGetBlockByNumber(new DefaultBlockParameterNumber(latestBlockNum), true)
                    .send()
                    .getBlock();

            //拿到块里面的所有交易结果对象
            List<EthBlock.TransactionResult> transactionResults= block.getTransactions();

            for (EthBlock.TransactionResult result : transactionResults) {
                //拿到交易对象
                EthBlock.TransactionObject transaction = (EthBlock.TransactionObject) result.get();

                //做自己真正的业务 --- 跟我们合约有关系的交易我们才处理
                if(contractAddress.equalsIgnoreCase(transaction.getTo().trim())){ //进入我们只能合约的
                    //根据我们的合约去写对应的逻辑
                    //根据交易哈希拿到交易详情数据
                    EthTransaction transactionDetail = web3j.ethGetTransactionByHash(transaction.getHash()).send();
                    System.out.println(transactionDetail);
                    //根据交易哈希拿到交易详情里面的日志数据
                    EthGetTransactionReceipt transactionReceipt =
                            web3j.ethGetTransactionReceipt(transaction.getHash()).send();
                    System.out.println(transactionReceipt);
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 使用web3j扫块 +过滤条件
     * @throws Exception
     * 这里条件是死的 而且扫块范围太大是会报错的 按道理应该是从库中读取起始扫块位作为start 获取最新的区块作为end 然后每次扫块结束更新start的位置
     */
    @PostMapping("listener")
    public void listener() throws Exception {

        //合约地址
        String contractAddress ="0x0B9b2f469232c761113a184B4148D2294C4C2DCf";
        Web3j web3j = web3jUtils.getBscWeb3j();

        //定义事件  -- Transfer事件
        Event event = new Event("Transfer",
                        Arrays.<TypeReference<?>>asList( //Transfer事件的参数类型
                                new TypeReference<Address>(true) {},
                                new TypeReference<Address>(true) {},
                                new TypeReference<Uint256>(false) {}
                )
        );

        //事件监听编码
        String eventHash = EventEncoder.encode(event);

        //这里为了方便写一个有我们合约自己交易的块高度
        BigInteger start = new BigInteger("27716137");
        BigInteger end = new BigInteger("27716138");

//        过滤器案例参考
//        EthFilter filter = new EthFilter(
//                new DefaultBlockParameterNumber(start),
//                new DefaultBlockParameterNumber(end),
//                contractAddress);
////                .addSingleTopic(eventHash);     //根据 事件过滤
////                        .addOptionalTopics("ox.....")  //根据 事件的topic哈希值过滤

        //指定监听规则： 合约地址 + 监听区块区间 + 指定事件
        EthFilter filter = new EthFilter(
                new DefaultBlockParameterNumber(start),
                new DefaultBlockParameterNumber(end),
                contractAddress).addSingleTopic(eventHash);

        EthLog send = web3j.ethGetLogs(filter).send();

        //得到所有的log结果对象
        List<EthLog.LogResult> allLogs = send.getLogs();
        allLogs.forEach(ll->{
            //得到Log对象
            EthLog.LogObject log = (EthLog.LogObject) ll.get();
            //得到触发该事件的合约地址
            System.out.println("address: ---->"+log.getAddress());
            //得到topics
            //topic0 是事件的签名  从topic1开始才是事件里面的具体的index标记的参数
            log.getTopics().forEach(t->{
                System.out.println("t--->" + t);
            });
            //log.data log.getData()返回的是16进制字符串
            System.out.println("data--->"+log.getData());
            String data = log.getData();
            BigInteger decimalData = new BigInteger(data.substring(2), 16); // 创建 BigInteger 对象，指定基数为 16
            String decimalString = decimalData.toString(); // 将16进制转换为十进制字符串
            System.out.println("decimalString--->"+decimalString);

            //data里面的数据是所有数据转成16进制然后拼接在一起的 所以如果只有一个参数的花直接截取掉前面的0x然后直接转换成10进制就好 一个数据是64的长度
            //但是当data里面有多个数据的时候 其实也是一样 把0x去掉 然后每64长度截取一个 每个再转成10进制就是你要的数据 参考如下
            //得到数据 --是16进制的 有4个参数假如data内
//            String data = log.getData();
//            System.out.println("data--->"+data);
//            //去掉16进制前面的0x
//            String substring = data.substring(2);
//            //每64长度是一个参数的值
//            String hexAmount0In = substring.substring(0,64);
//            String hexAmount1In = substring.substring(64,128);
//            String hexAmount0Out = substring.substring(128,192);
//            String hexAmount1Out = substring.substring(192,256);
//            //把16进制转为10进制 就是实际的值
//            System.out.println(new BigInteger(hexAmount0In, 16));
//            System.out.println(new BigInteger(hexAmount1In, 16));
//            System.out.println(new BigInteger(hexAmount0Out, 16));
//            System.out.println(new BigInteger(hexAmount1Out, 16));

        });

        // 而且扫块范围太大报错参考
//        //从头扫到尾  -- 应该是从库中读取起始扫块位
//        BigInteger start = BigInteger.ZERO;
//        //获取最新的区块
//        BigInteger end = web3j.ethBlockNumber().send().getBlockNumber();
//
//        //合约地址列表
//        ArrayList<String> addressList = new ArrayList<>();
//        addressList.add(contractAddress);
//
//        EthLog send = web3jUtils.getEthLogs(web3j, start, end, addressList, eventHash, null);
//        while (send.hasError()){
//            //bsc这个链扫块的范围太大会报错 动态缩小范围
//            if(send.getError().getCode() == -32600){
//                BigInteger size = end.subtract(start);
//                if (start.equals(end)) {
//                    throw new RuntimeException("获取单个区块logs时发生错误，请联系管理员");
//                }
//                size = size.divide(new BigInteger("2"));
////                end = start.add(size);
//                start = start.add(size);
//                send = web3jUtils.getEthLogs(web3j, start,end,addressList,eventHash,null);
//            }else {
//                break;
//            }
//        }
//
//        //得到所有的log结果对象
//        List<EthLog.LogResult> allLogs = send.getLogs();
//        if (CollectionUtil.isNotEmpty(allLogs)) {
//            allLogs.forEach(ll->{
//                //得到Log对象
//                EthLog.LogObject log = (EthLog.LogObject) ll.get();
//                //log的address = 合约地址
//                System.out.println("address: ---->"+log.getAddress());
//                //log的topics
//                log.getTopics().forEach(t->{
//                    System.out.println("t--->" + t);
//                });
//                //log.data
//                System.out.println("data--->"+log.getData());
//                //blockNumber
//                System.out.println(log.getBlockNumber());
//            });
//            //循环完成后记录一下扫到了多少块保存起来当下次的起始块来用
//        }


    }



    //--------------------------以下是一些其他对web3j封装的包的案例------------------------------


    /**
     * 使用 Magician-ContractsTools 测试访问 智能合约 read方法 --获取智能合约代币余额
     * @throws Exception
     */
    @PostMapping("bnbContract")
    public void bnbContract() throws Exception {
        // 私钥
//        String privateKey = "3403038c4d97d23bd67bb26e0015e123bf869b22c38f5b2f6bc7c0b70e9e371b";
        //RPC地址
        Web3j web3j = web3jUtils.getBscWeb3j();
        //合约地址
        String contractAddress = "0x0B9b2f469232c761113a184B4148D2294C4C2DCf";
        EthContractUtil ethContractUtil = EthContractUtil.builder(web3j);
        List<Type> result = ethContractUtil.select(
                contractAddress, // 合约地址
                //  方法名称  参数    方法的返回类型
                EthAbiCodecTool.getInputData(
                        "balanceOf",
                        new Address("0x71c77511401b1C7CB64c89823f54C5928C5A624A")
                ),
                new TypeReference<Uint256>(){}
        );
        result.stream().forEach(e ->{
                    System.out.print(e.getValue() + " <-------> " + e.getTypeAsString());
                }
        );
    }

    /**
     * 使用 Magician-ContractsTools 测试访问 智能合约 wirte方法 -- 转账
     * @throws Exception
     * 会报错 原因是因为签名需要带上chainId 但是目前还不知道如何解决
     */
    @PostMapping("bnbContractWrite")
    public void bnbContractWrite() throws Exception {
        // 私钥
        String privateKey = "3403038c4d97d23bd67bb26e0015e123bf869b22c38f5b2f6bc7c0b70e9e371b";
        Web3j web3j = web3jUtils.getBscWeb3j();
        //合约地址
        String contractAddress = "0x0B9b2f469232c761113a184B4148D2294C4C2DCf";
        EthContractUtil ethContractUtil = EthContractUtil.builder(web3j);
        SendResultModel sendResultModel = ethContractUtil.sendRawTransaction(
                SendModel.builder()
                        .setSenderAddress("0x71c77511401b1C7CB64c89823f54C5928C5A624A") // 调用者的地址
                        .setPrivateKey(privateKey)// 调用者地址私钥
                        .setToAddress(contractAddress) // 合约地址
                        .setValue(null) // 主链币数量，如果想用默认值 可以直接传null，或者不传这个参数
                        .setGasPrice(null) // gasPrice，如果想用默认值 可以直接传null，或者不传这个参数
                        .setGasLimit(null) // gasLimit，如果想用默认值 可以直接传null，或者不传这个参数
                        .setNonce(null), // 自定义nonce，如果想要默认值 可以直接传null，或者不传这个参数
                //
                EthAbiCodecTool.getInputData(
                        "transfer", // 要调用的方法名称
                        new Address("0x45998d65609965978F9Da29A9b7dB424aC45e682"), // 方法的参数，如果有多个，可以继续传入下一个参数
                        new Uint256(new BigInteger("1000")) // 方法的参数，如果有多个，可以继续传入下一个参数
                ) // 要调用的方法的inputData
        );
        EthSendTransaction ethSendTransaction = sendResultModel.getEthSendTransaction();// 发送交易后的结果
        EthGetTransactionReceipt ethGetTransactionReceipt = sendResultModel.getEthGetTransactionReceipt();// 交易成功上链后的结果

        log.info("ethSendTransaction:{}",ethSendTransaction.getTransactionHash());
        log.info("ethGetTransactionReceipt:{}",ethGetTransactionReceipt.getTransactionReceipt());

    }

    /**
     * 开启扫块
     * 使用Magician-Scanning扫块里面符合监听逻辑的交易
     */
    @PostMapping("scan")
    public void scan()  {
        // 初始化线程池，核心线程数必须 >= 扫块的任务数量，建议等于扫块的任务数量
        EventThreadPool.init(1);

        // 开启一个扫块任务，如果你想扫描多个链，那么直接拷贝这段代码，并修改配置即可
        try {
            System.out.println("开启扫描开启");
            EthRpcInit ethRpcInit = new EthRpcInit();
            ethRpcInit.addRpcUrl("https://rpc.ankr.com/bsc_testnet_chapel");
            MagicianBlockchainScan.create()
                    .setRpcUrl(ethRpcInit) // 节点的 RPC 地址
//                .setChainType(ChainType.ETH) // 要扫描的链（如果设置成 ETH ，那么可以扫描 BSC, POLYGAN 等其他任意 以太坊标准的链）
                    .setScanPeriod(5000) // 每轮扫描的间隔
                    .setBeginBlockNumber(BigInteger.valueOf(27716137l)) // 从哪个块高开始扫描
                    .addEthMonitorEvent(new EventDemo()) // 添加 监听事件
                    .start();
            System.out.println("开启扫描结束");
        }catch (Exception e){
            e.printStackTrace();
        }

    }

    /**
     * 使用ipfs上传文件
     * @throws IOException
     */
    @PostMapping("ipfsUpload")
    public void ipfsUpload() throws IOException {
        // filePath是要上传文件路径
        String filePath = "D:\\test\\test.png";
        // 上传完成后会返回一个hash 相当于这个图片地址
        String cid = ipfsUtil.upload(ipfs, filePath);

    }

    /**
     * 使用ipfs下载文件
     * @throws IOException
     */
    @PostMapping("ipfsDownload")
    public void ipfsDownload() throws IOException {
        // 图片地址
        String cid = "QmYR2UrDS461NYG8LN7TkvQtSp6ejLmMeC22dTsJJa2swv";
        // 下载位置  下载的文件就会在这个位置
        String destFile = "D:\\test\\testTest.png";
        ipfsUtil.download(ipfs, cid, destFile);
    }












}
